home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CD ROM Paradise Collection 4
/
CD ROM Paradise Collection 4 1995 Nov.iso
/
program
/
4cmp22s.zip
/
MULTID.4TH
< prev
next >
Wrap
Text File
|
1994-10-30
|
17KB
|
642 lines
\ ForthCMP Multitasking Module
\ Copyright 1985, 1993 (C) By Thomas Almy. All rights reserved.
\ Permission is granted to registered users of ForthCMP to sell or distribute
\ computer programs incorporating the compiled contents of this file.
\ This module writes direct to the display for terminal I/O
.( LOADING MULTID) CR
FIND EMIT? [IF] DROP 1 [ELSE] 0 [THEN] CONSTANT facl \ FACILITY Wordset used
INCLUDE INTS
INCLUDE FARMEM1
10 HEX
\ If EGA is defined non-zero then 43 line EGA code is generated
FIND EGA [IF] DROP [ELSE] 0 CONSTANT EGA [THEN]
\ If VGA is defined non-zero then 50 line VGA code is generated
FIND VGA [IF] DROP [ELSE] 0 CONSTANT VGA [THEN]
EGA 0<> VGA 0<> OR CONSTANT ENHANCED
ENHANCED [IF] 0 CONSTANT VID-DELAY [THEN] \ no vid delay if EGA or VGA
ENHANCED 0= [IF] VARIABLE crtport 3D4 crtport ! [THEN]
\ If VID-DELAY is defined non-zero then anti-snow code is added
FIND VID-DELAY [IF] DROP [ELSE] 0 CONSTANT VID-DELAY [THEN]
VARIABLE vidseg \ VIDEO SEGMENT
B800 vidseg !
50 CONSTANT c/l \ Characters per line
EGA [IF] 2B [ELSE] VGA [IF] 32 [ELSE] 19 [THEN] [THEN]
CONSTANT l/s \ lines per screen
DECIMAL
0 0 IN/OUT NEED SINGLE
0 0 IN/OUT NEED MULTI
0 0 IN/OUT NEED PAUSE
0 0 IN/OUT NEED end-timer
0 0 IN/OUT NEED start-timer
0 0 IN/OUT NEED PAGE
VARIABLE ?multi \ true if multitasking turned on
VARIABLE user \ disp into user segment--used at comp time
VARIABLE CTASK \ pointer to task list
VARIABLE inaccept \ executing EXPECT -- only one at a time, please!
\ Semaphores
1 0 IN/OUT
: SEMA BEGIN DUP @ WHILE PAUSE REPEAT ON ;
1 0 IN/OUT
: PHORE OFF PAUSE ;
0 0 IN/OUT
: BYE unsetup-vid end-timer bye ;
\ Memory management interface
1 1 IN/OUT
: GET malloc IF ." OUT OF MEMORY " BYE THEN ;
\ USER VARIABLES
H: UALLOT DSEG user @ + user ! ;
1 2 IN/OUT
H: UCREATE user @ CONSTANT ;
H: UVARIABLE UCREATE 2 UALLOT ;
H: URESET DSEG 0 user ! ;
URESET
\ redefinition of primitive I/O functions
HEX
1 0 IN/OUT
: storecursor ( DISPL -- ) CTASK @ 12 + CS: ! ;
1 0 IN/OUT
: setcursor ( DISPL -- )
ENHANCED [IF]
2/ DUP 0F 3D4 PC! 3D5 PC! >< 0E 3D4 PC! 3D5 PC!
[ELSE]
2/ DUP 0F crtport @ PC! crtport @ 1+ PC!
>< 0E crtport @ PC! crtport @ 1+ PC!
[THEN]
;
0 0 IN/OUT
: nocursor l/s c/l * 2* 1- setcursor ( OFF SCREEN ! ) ;
2 0 IN/OUT
: AT-XY c/l * + 2* storecursor ;
ENHANCED [IF]
0 0 IN/OUT
EGA [IF]
CODE set-ega
03 # AX MOV 10 INT \ SET MODE 3
1112 # AX MOV 0 # BL MOV 10 INT \ Load 8X8 font
1200 # AX MOV 20 # BL MOV 10 INT \ Load new printscreen
1 # AH MOV 707 # CX MOV 10 INT \ LOAD CURSOR SCAN LINES
3D4 # DX MOV 0A # AL MOV [DX] BYTE OUT \ set cursor
FWD, THEN,
DX INC
6 # AL MOV [DX] OUT
RET
END-CODE
[ELSE] \ must be VGA
CODE set-ega
1202 # AX MOV 30 # BL MOV 10 INT \ 400 scan lines
03 # AX MOV 10 INT \ SET MODE 3
1112 # AX MOV 0 # BL MOV 10 INT \ Load 8X8 font
1200 # AX MOV 20 # BL MOV 10 INT \ Load new printscreen
RET
END-CODE
[THEN]
0 0 IN/OUT
CODE unset-ega
VGA [IF]
1201 # AX MOV 30 # BL MOV 10 INT \ 350 scan lines
[THEN]
03 # AX MOV 10 INT RET END-CODE
[THEN]
0 0 IN/OUT
: setup-vid
ENHANCED [IF]
set-ega
CTASK @ 12 + CS: OFF \ home cursor
[ELSE]
40 49 C@L 7 = IF 3B4 crtport ! B000 vidseg ! THEN \ MONOCHROME
40 50 C@L 40 51 C@L AT-XY
vidseg @ c/l l/s 1- * 2* 1+ C@L CTASK @ 14 + CS: !
[THEN]
;
CODE unsetup-vid
ENHANCED [IF]
CALL' PAGE
CALL' unset-ega
DX DX XOR
[ELSE]
CTASK [] BX MOV
CS: 12 +[BX] AX MOV \ cursor offset
c/l # BX MOV
DX DX XOR
AX 1 SAR
BX IDIV
AL DH MOV
[THEN]
2 # AH MOV
BH BH XOR
10 INT
RET
END-CODE \ unsetup-vid
CODE scrmove ( source dest wordCount -- )
BX POP
CX POP
DI POP
SI POP
LOOP IF,
DS PUSHSEG
VID-DELAY [IF]
B800 # vidseg [] CMP =0 IF,
3DA # DX MOV
BEGIN,
BYTE [DX] IN
8 # AL TEST
=0 ~ UNTIL,
DX DEC
DX DEC
21 # AL MOV
BYTE [DX] OUT
THEN,
[THEN]
vidseg [] AX MOV
AX DS >SEG
AX ES >SEG
REPZ MOVS
DS POPSEG
VID-DELAY [IF]
B800 # vidseg [] CMP =0 IF,
3D8 # DX MOV
29 # AL MOV
BYTE [DX] OUT
THEN,
[THEN]
THEN,
BX JMPI
END-CODE \ scrmove
2 0 IN/OUT
CODE scrfill ( source wordCount -- )
vidseg [] ES >SEG
20 # BYTE ES: [BX] MOV
CTASK [] DI MOV
CS: 14 +[DI] CL MOV \ style
CL ES: 1 +[BX] MOV
BX PUSH
BX INC
BX INC
BX PUSH
AX DEC
AX PUSH
CALL' scrmove
RET
END-CODE \ scrfill
0 0 IN/OUT
: scrollup c/l 2* 0 c/l l/s 1- * scrmove
c/l l/s 1- * 2* c/l scrfill
c/l l/s 1- * 2* CTASK @ 12 + CS: ! ( set cursor ) ;
0 2 IN/OUT
: ?XY CTASK @ 12 + CS: @ 2/ 0 c/l UM/MOD ;
1 0 IN/OUT
: FOREGROUND 0F AND CTASK @ 14 + TUCK CS: @ F0 AND OR SWAP CS: ! ;
1 0 IN/OUT
: BACKGROUND 7 AND 4 LSHIFT CTASK @ 14 + TUCK CS: @ 0F AND OR SWAP CS: ! ;
: EMIT
CTASK @ 12 + CS: @ c/l l/s * 2* >= IF scrollup THEN
vidseg @ CTASK @ 12 + CS: @ C!L
CTASK @ 14 + CS: @ vidseg @ CTASK @ 12 + CS: @ 1+ C!L
CTASK @ 12 + CS: @ CELL+ storecursor PAUSE ;
: CR
CTASK @ 12 + CS: @
c/l 2* U/ 1+ c/l 2* *
DUP c/l l/s * 2* = IF DROP scrollup CTASK @ 12 + CS: @ THEN
storecursor PAUSE ;
: SPACES
DUP 0> IF
c/l l/s * 2* CTASK @ 12 + CS: @ - OVER 2* < IF ( too big )
0 DO BL EMIT LOOP ELSE
CTASK @ 12 + CS: @ SWAP 2DUP scrfill
2* + storecursor PAUSE
THEN
ELSE DROP
THEN
;
2 1 IN/OUT
CODE (type) ( AX has count, BX has string, result is cursor position )
BX SI MOV
CTASK [] BX MOV
CS: 12 +[BX] DI MOV \ cursor
AX CX MOV
CS: 14 +[BX] AH MOV \ style
vidseg [] ES >SEG
LOOP IF,
BEGIN,
BYTE LODS
STOS
LOOP ~ UNTIL,
THEN,
DI AX MOV \ final cursor position
RET
END-CODE \ (type)
: TYPE
c/l l/s * 2* CTASK @ 12 + CS: @ - OVER 2* < IF ( too big )
0 ?DO COUNT EMIT LOOP DROP
ELSE
(type) storecursor PAUSE
THEN ;
2 1 IN/OUT
CODE (cs:type) ( AX has count, BX has string, result is cursor position)
BX SI MOV
CTASK [] BX MOV
CS: 12 +[BX] DI MOV \ cursor
AX CX MOV
CS: 14 +[BX] AH MOV \ style
vidseg [] ES >SEG
LOOP IF,
BEGIN,
CS: BYTE LODS
STOS
LOOP ~ UNTIL,
THEN,
DI AX MOV \ final cursor position
RET
END-CODE \ (cs:type)
: CS:TYPE
c/l l/s * 2* CTASK @ 12 + CS: @ - OVER 2* < IF ( too big )
0 ?DO CS: COUNT EMIT LOOP DROP
ELSE
(cs:type) storecursor PAUSE
THEN ;
0 0 IN/OUT
: PAGE 0 c/l l/s * scrfill 0 storecursor ;
0 1 IN/OUT
facl [IF]
CODE EKEY?
[ELSE]
CODE KEY?
[THEN]
CALL' PAUSE \ allow another task to execute
1 # AH MOV
16 INT
0 # AX MOV
=0 ~ IF, AX DEC THEN,
RET
END-CODE \ KEY?
: PAD CTASK @ 18 + CS: @ ;
facl [IF]
VARIABLE pchr -1 pchr !
: KEY pchr @ 0< 0= IF pchr @ pchr ON EXIT THEN
BEGIN EKEY EKEY>CHAR 0= WHILE DROP REPEAT ;
: KEY? pchr @ 0< 0= IF TRUE EXIT THEN
BEGIN EKEY? CTASK @ 12 + CS: @ setcursor WHILE
EKEY EKEY>CHAR IF pchr ! TRUE EXIT THEN DROP
REPEAT FALSE ;
: EKEY BEGIN EKEY? CTASK @ 12 + CS: @ setcursor UNTIL 0 7 BDOS
?DUP 0= IF BEGIN EKEY? CTASK @ 12 + CS: @ setcursor UNTIL
0 7 BDOS 256 + THEN ;
[ELSE]
: KEY BEGIN KEY? CTASK @ 12 + CS: @ setcursor UNTIL
0 8 BDOS
PAUSE
nocursor ;
[THEN]
\ ACCEPT
0 0 IN/OUT
: bu CTASK @ 12 + CS: @ CELL- DUP storecursor BL EMIT storecursor ;
DECIMAL
: ACCEPT
inaccept SEMA \ too hard if two or more tasks want input at once!
>R 0
BEGIN
KEY CASE
[CTRL] [ OF 0 ?DO bu LOOP 0 ENDOF
[CTRL] H OF DUP IF bu 1- THEN ENDOF
[CTRL] M OF
NIP R> DROP
inaccept PHORE
EXIT ENDOF
( ELSE ) OVER R@ <> IF DUP >R EMIT
2DUP + R> SWAP C! 1+ 0 THEN
ENDCASE
AGAIN ;
\ TASK CREATION
HEX
H: TASK \ values after INIT-TASKS:
CSEG CREATE HERE E92E , \ DISP 0 -- JMP ( task asleep )
DSEG CTASK @ , CTASK ! \ 02 -- relative addr nxt task
user @ , \ 04 -- size of user area (not used?)
0 , \ 06 -- SS register contents
user @ pssize 10 * + , \ 08 -- SP register contents
user @ pssize 10 * + rssize + , \ 0A -- BP register contents
, \ 0C -- PC contents
\ the following fields are for per-task variables
\ and could be selectively elimiated if not needed if space is
\ at a premium. In that case, offsets may need to be adjusted
\ for words which use latter fields.
0 , \ 0E -- Message list
0 , \ 10 -- Timer
0 , \ 12 -- Cursor location
7 , \ 14 -- character attribute (style)
0 , \ 16 -- Exception frame pointer
DSEG HERE 80 ALLOT 22 + , \ 18 -- PAD, a per-task work area
;
0 [IF]
Initially, DISP 2 has absolute address of next task.
This values as well as DISP 6 get
filled in by INIT-TASKS when application is run.
[THEN]
CSEG CREATE MAIN-TASK \ Give it a name
HERE DSEG CTASK ! \ Task list points to it
80CD , \ DISP 0 -- INT 80 (task awake)
0 , \ 02 -- relative addr next task
0 , \ 04 -- NOT USED
0 , \ 06 -- SS register contents
0 , \ 08 -- SP register contents
0 , \ 0A -- BP register contents
0 , \ 0C -- PC contents
0 , \ 0E -- Message list
0 , \ 10 -- Timer
0 , \ 12 -- Cursor Location
7 , \ 14 -- Style
0 , \ 16 -- Exception Frame Pointer
DSEG HERE 80 ALLOT 22 + , \ 16 -- PAD, a per-task work area
0 [IF]
DISP-2, 6, and 12 get filled in by INIT-TASK. -8 -0A and -0C
are filled by first task swap (which is done by INIT-TASK).
[THEN]
\ TASK INITIALIZATION
0 0 IN/OUT
: INIT-TASKS \ This MUST be executed to start multitasking
CTASK @
BEGIN ?DUP WHILE \ for each task DO:
CELL+ DUP CS: @ IF \ one follows, this isn't main task
DUP 8 + CS: @ 10 + 4 RSHIFT GET
OVER 4 + CS: ! \ stackseg
DUP CS: @ TUCK \ next task
ELSE
0 SWAP CTASK @ \ next task is head of list
THEN
OVER - CELL- SWAP CS: !
REPEAT
MAIN-TASK CTASK !
setup-vid
?SS: MAIN-TASK 6 + CS: ! \ sets main task stack segment
start-timer
MULTI ( GO!!! ) ;
\ TASK DISPATCHER
CODE PAUSE
0 # ?multi [] CMP
=0 IF, RET THEN,
CTASK [] BX MOV \ current task
CS: 0C +[BX] POP \ save PC
BP CS: 0A +[BX] MOV \ save BP
SP CS: 08 +[BX] MOV \ save SP
CS: 2 +[BX] BX ADD
4 # BX ADD
CLI \ no ints during dispatch!
BX JMPI ( dispatch )
END-CODE \ PAUSE
0 [IF]
Tasks are linked together so that jumping to a task will cause
jumping to the next if it is asleep, or doing an INT 80 if it
is awake. Thanks to Henry Laxen's Forth 83 model for the
technique.
[THEN]
L: start-task ( the INT80 routine )
BX POP
BX DEC
BX DEC \ Pointer to the task
CS: 6 +[BX] SS >SEG \ restore stack segment
CS: 8 +[BX] SP MOV \ restore SP
STI \ Interrupts are safe now
CS: 0A +[BX] BP MOV \ restore BP
BX CTASK [] MOV \ current task
CS: 0C +[BX] JMPI \ go!
FORTH \ start-task
0 [IF]
This code starts up a new task by setting up all registers,
fixing CTASK and USERP, and jumping to where we left off.
[THEN]
\ TASK MANAGEMENT
: SINGLE ?multi OFF ;
: MULTI ?multi ON
?CS: start-task 80 set-handler \ install interrupt vector
PAUSE \ start with a task swap
;
1 0 IN/OUT
: WAKE 80CD CS: <- ;
1 0 IN/OUT
\ the 2e prefix byte (CS override) makes the jmp instruction 4 bytes long
: SLEEP ( task -- ) E92E CS: <- ;
1 1 IN/OUT
: WAITING? 10 + CS: @ 0<> ;
0 0 IN/OUT
: STOP CTASK @ SLEEP PAUSE ;
0 1 IN/OUT
: ACTIVE-TASKS
0 MAIN-TASK
BEGIN
DUP WAITING? IF SWAP 1+ SWAP ELSE
DUP CS: @ 80CD = IF SWAP 1+ SWAP THEN THEN \ check for active
DUP CELL+ CS: @ + 4 + \ address of next task
DUP MAIN-TASK = UNTIL \ Loop until back to start
DROP ( task address )
;
\ MESSAGE PASSING
0 1 IN/OUT
: MESSAGE? CTASK @ 0E + CS: @ ;
0 1 IN/OUT
: GET-MESSAGE
BEGIN MESSAGE? ?DUP 0= WHILE STOP REPEAT
DUP 0 @L CTASK @ 0E + CS: ! \ Unlink message
;
1 1 IN/OUT
: MESSAGES
0 SWAP 0E + CS: @ ?DUP IF
BEGIN SWAP 1+ SWAP 0 @L ?DUP 0= UNTIL
THEN ;
2 0 IN/OUT
: SEND-MESSAGE
OVER 0 SWAP 0 !L \ set message's next field to NIL
DUP WAITING? 0= IF DUP WAKE THEN \ fire up receiving task
\ unless waiting for timer
0E + DUP CS: @ ?DUP IF \ Existing messages in queue
NIP
BEGIN DUP 0 @L ?DUP WHILE NIP REPEAT \ find end of list
0 !L \ store message at end of list
ELSE
CS: ! \ no existing messages, put at head of queue.
THEN
PAUSE ; \ Give it a chance to run
\ control-break handler
\ always gets control and (currently) dumps task information
2VARIABLE cb_save
1B CONSTANT cb_int
0 0 IN/OUT
: cbt
PAGE
SINGLE
end-timer
." Task statistics: "
MAIN-TASK \ start with first
BEGIN CR
HEX DUP 0 <# # # # # #> TYPE SPACE \ address
DUP WAITING? IF ." Waiting " DUP 10 + CS: @ . ." ticks" ELSE
DUP CS: @ 80CD = IF ." Active" ELSE ." Sleeping" THEN THEN
DUP CELL+ CS: @ + 4 + \ address of next task
DUP MAIN-TASK = UNTIL \ Loop until back to start
DROP ( task address )
EGA [IF]
CR ." Hit any key when finished" KEY DROP
[THEN]
unsetup-vid
bye
;
' cbt TASK cb-task
L: cb_handler ( actual interrupt handler )
80CD # CS: cb-task [] MOV \ wake cb task
STI
IRET FORTH
\ timer
1C CONSTANT t_int \ timer interupt vector number
CSEG
CREATE t_save 4 ALLOT \ original interupt vector
L: t_handler
PUSHF CS: t_save CALLF \ do original functions
BX PUSH
MAIN-TASK # BX MOV ( start of list )
BEGIN,
CS: 0 # 10 +[BX] CMP =0 ~ IF, ( non_zero time )
CS: 10 +[BX] DEC ( count down )
=0 IF, 80CD # CS: [BX] MOV THEN, ( wake task )
THEN,
CS: 2 +[BX] BX ADD
4 # BX ADD ( next task )
MAIN-TASK # BX CMP
=0 UNTIL, ( back at start? )
BX POP
IRET
FORTH \ t_handler
\ timer start and end 08:09 11/18/85
: start-timer \ and control break handler
t_int get-handler t_save CS: 2!
?CS: t_handler t_int set-handler
cb_int get-handler cb_save 2!
?CS: cb_handler cb_int set-handler
;
: end-timer
t_save CS: 2@ t_int set-handler
cb_save 2@ cb_int set-handler
;
2 0 IN/OUT
: TIME-OUT ( ticks task -- ) DUP SLEEP 10 + CS: ! ;
1 0 IN/OUT
DECIMAL
: MS ( ticks -- ) 182 10000 */ CTASK @ TIME-OUT PAUSE ;
HEX
\ Exception Wordset
CODE CATCH
SI POP AX POP \ retAddr execAddr
CTASK [] BX MOV
BP DEC BP DEC SI [BP] MOV
BP DEC BP DEC SP [BP] MOV
BP DEC BP DEC CS: 16 +[BX] CX MOV CX [BP] MOV
BP CS: 16 +[BX] MOV
AX CALLI
[BP] AX MOV AX CS: 16 +[BX] MOV
AX AX XOR AX PUSH
4 +[BP] AX MOV 6 # BP ADD
AX JMPI
END-CODE
1 0 IN/OUT
CODE throw
CTASK [] BX MOV
CS: 16 +[BX] BP MOV [BP] BX MOV BX CS: 16 +[BX] MOV
2 +[BP] SP MOV AX PUSH
4 +[BP] AX MOV
6 # BP ADD AX JMPI
END-CODE
: THROW ?DUP IF CTASK @ 16 + CS: @ IF throw THEN
." Uncaught THROW: " . BYE THEN ;
DSEG 0A = [IF] DECIMAL [THEN]